#!/usr/bin/python
# -*- coding: utf-8 -*-
#~ '****************************************************************************
#~ '    CLASE PARA CONSUMIR LOS SERVICIOS DEL EXCELENTE PAC ECODEX
#~ '
#~ '    Copyright (C) 2012 Mauricio Baeza Servin
#~ '    Este programa es software libre. Puede redistribuirlo y/o modificarlo
#~ '    bajo los términos de la Licencia Pública General de GNU según es
#~ '    publicada por la Free Software Foundation, bien de la versión 3 de dicha
#~ '    Licencia o bien (según su elección) de cualquier versión posterior.
#~ '
#~ '    Este programa se distribuye con la esperanza de que sea útil, pero SIN
#~ '    NINGUNA GARANTÍA, incluso sin la garantía MERCANTIL implícita o sin
#~ '    garantizar la CONVENIENCIA PARA UN PROPÓSITO PARTICULAR.
#~ '    Véase la Licencia Pública General de GNU para más detalles.
#~ '
#~ '    Debería haber recibido una copia de la Licencia Pública General junto
#~ '    con este programa. Si no ha sido así, escriba a la Free Software
#~ '    Foundation, Inc., en 675 Mass Ave, Cambridge, MA 02139, EEUU.
#~ '
#~ '    Mauricio Baeza - mauricio ARROBA correolibre.net
#~ '    20-Agosto-2013 - Actualización a la version 2 de los WebServices Ecodex
#~ '
#~ '****************************************************************************

""" Class for webservices PAC ECODEX v2"""

import hashlib
import random
import base64
import os
#~ import time
from pysimplesoap.client import SoapClient, SoapFault
from pysimplesoap.simplexml import SimpleXMLElement

PREFIX_NS = 'cfdi'
PREFIX_SOAP = 'soapenv'


class ECODEX():
    # Reemplazar por el ID de integrador que te asigne ECODEX
    _rfc_integrador = 'BBB010101001'
    _id_integrador = '2b3a8764-d586-4543-9b7e-82834443f219'
    _id_alta_emisor = 'DF627BC3-A872-4806-BF37-DBD040CBAC7C'
    _servicio_seguridad = 'https://wsdexpruebas.ecodex.com.mx:2045/ServicioSeguridad.svc?wsdl'
    _servicio_comprobantes = 'https://wsdexpruebas.ecodex.com.mx:2045/ServicioComprobantes.svc?wsdl'
    _servicio_timbrado = 'https://wsdexpruebas.ecodex.com.mx:2045/ServicioTimbrado.svc?wsdl'
    _servicio_repositorio = 'https://wsdexpruebas.ecodex.com.mx:2045/ServicioRepositorio.svc?wsdl'
    _servicio_clientes = 'https://wsdexpruebas.ecodex.com.mx:2045/ServicioClientes.svc?wsdl'
    _servicio_cancelacion = 'https://wsdexpruebas.ecodex.com.mx:2045/ServicioCancelacion.svc?wsdl' 
    _url_certificado = 'https://servicios.ecodex.com.mx/Certificados/Upload?UUID=%s'
    
    def __init__(self, rfc, solo_timbrar=False):
        self.rfc = rfc
        self.solo_timbrar = solo_timbrar
        self.error = None
        self.xml_send = ''
        self.xml = ''
        self.qr = None

    def _getToken(self, id_transaccion=0, alta=False):
        try:
            client = SoapClient(
                wsdl = self._servicio_seguridad, 
                ns = PREFIX_NS,
                soap_ns = PREFIX_SOAP
            )
            if alta:
                rfc = self._rfc_integrador
            else:
                rfc = self.rfc
            retval = client.ObtenerToken(
                RFC = rfc, 
                TransaccionID = id_transaccion
            )
        except SoapFault as sf:
            self.error = sf
            return False
        else:
            if alta:
                token_usuario = hashlib.sha1(
                    '%s|%s|%s' % (
                        self._id_integrador, 
                        self._id_alta_emisor, 
                        retval['Token'])).hexdigest()
            else:
                token_usuario = hashlib.sha1(
                    '%s|%s' % (
                        self._id_integrador, 
                        retval['Token'])).hexdigest()
            return token_usuario
            
    def TimbrarXML(self, id_cfdi=0):
        #~ start_time = time.time()
        if id_cfdi:
            id_transaccion = id_cfdi
        else:
            id_transaccion = random.randint(1, 1000000000)
        token = self._getToken(id_transaccion)
        if not token:
            return False
        try:
            servicio = self._servicio_comprobantes
            metodo = 'SellaTimbraXML'
            if self.solo_timbrar:
                servicio = self._servicio_timbrado
                metodo = 'TimbraXML'
            xml = self.xml_send.encode("ascii", "xmlcharrefreplace")
            client = SoapClient(wsdl=servicio, ns=PREFIX_NS, soap_ns=PREFIX_SOAP)
            retval = getattr(client, metodo)(
                ComprobanteXML = {'DatosXML': xml},
                RFC = self.rfc,
                Token = token, 
                TransaccionID = id_transaccion
            )
        except SoapFault as sf:
            self.error = (sf.faultstring,)
            return False
        else:
            x = SimpleXMLElement(retval['ComprobanteXML']['DatosXML'])
            self.xml = x.as_xml(pretty=True)
            #~ print time.time() - start_time, "seconds"
            return True
            
    def CancelacionesXML(self, uuids, id_cfdi=0):
        if id_cfdi:
            id_transaccion = id_cfdi
        else:
            id_transaccion = random.randint(1, 1000000000)
        token = self._getToken(id_transaccion)
        if not token:
            return False
        try:
            lista = []
            for u in uuids:
                lista.append({'guid': u})
            client = SoapClient(
                wsdl = self._servicio_cancelacion,
                ns = PREFIX_NS,
                soap_ns = PREFIX_SOAP
            )
            retval = client.CancelaMultiple(
                ListaCancelar = lista,
                RFC = self.rfc,
                Token = token, 
                TransaccionID = id_transaccion
            )
            return retval['Resultado']
        except SoapFault as sf:
            self.error = sf
            return False

    def CancelacionesOtrosXML(self, rfc_receptor, uuid, id_cfdi=0):
        if id_cfdi:
            id_transaccion = id_cfdi
        else:
            id_transaccion = random.randint(1, 1000000000)
        token = self._getToken(id_transaccion)
        if not token:
            return False
        try:
            client = SoapClient(
                wsdl = self._servicio_cancelacion,
                ns = PREFIX_NS,
                soap_ns = PREFIX_SOAP
            )
            retval = client.CancelaOtros(
                RFCEmisor = self.rfc,
                RFCReceptor = rfc_receptor,
                Token = token, 
                TransaccionID = id_transaccion,
                UUID = uuid 
            )
            return retval['Resultado']
        except SoapFault as sf:
            self.error = sf
            return False

    def RecuperarAcuses(self, uuid):
        id_transaccion = random.randint(1, 1000000000)
        token = self._getToken(id_transaccion)
        if not token:
            return False
        try:
            client = SoapClient(
                wsdl = self._servicio_cancelacion,
                ns = PREFIX_NS,
                soap_ns = PREFIX_SOAP
            )
            retval = client.RecuperarAcuses(
                RFC = self.rfc,
                Token = token, 
                TransaccionID = id_transaccion,
                UUID = uuid 
            )
            x = SimpleXMLElement(retval['AcuseXML'])
            self.xml = x.as_xml(pretty=True)
            return retval
        except SoapFault as sf:
            self.error = sf
            return False
            
    # Va a desaparecer
    def CancelarXML(self, uuid, id_cfdi=0):
        if id_cfdi:
            id_transaccion = id_cfdi
        else:
            id_transaccion = random.randint(1, 1000000000)
        token = self._getToken(id_transaccion)
        if not token:
            return False        
        try:
            servicio = self._servicio_repositorio
            metodo = 'CancelaComprobante'
            if self.solo_timbrar:
                servicio = self._servicio_timbrado
                metodo = 'CancelaTimbrado'
            client = SoapClient(wsdl=servicio, ns='cfdi', soap_ns='soapenv')
            retval = getattr(client, metodo)(
                RFC = self.rfc,
                Token = token, 
                TransaccionID = id_transaccion,
                UUID=uuid
            )
        except SoapFault as sf:
            self.error = sf
            return False
        else:            
            return retval['Cancelada']          
            
    def ObtenerXML(self, uuid='', id_original=0):
        if not uuid and not id_original:
            self.error = 'Se requiere el UUID o el ID de la transacción original'
            return False
        if uuid:
            id_original = 0
        id_transaccion = random.randint(1, 1000000000)
        token = self._getToken(id_transaccion)
        if not token:
            return False
        try:
            servicio = self._servicio_repositorio
            metodo = 'ObtenerComprobante'
            if self.solo_timbrar:
                servicio = self._servicio_timbrado
                metodo = 'ObtenerTimbrado'
            client = SoapClient(wsdl=servicio, ns='cfdi', soap_ns='soapenv')
            retval = getattr(client, metodo)(
                RFC = self.rfc,
                Token = token, 
                TransaccionID = id_transaccion,
                TransaccionOriginal = id_original,
                UUID=uuid
            )
            x = SimpleXMLElement(retval['ComprobanteXML']['DatosXML'])
            self.xml = x.as_xml(pretty=True)
            return True
        except SoapFault as sf:
            self.error = sf.faultcode, sf.faultstring, sf.message
            return False
            
    def ObtenerQR(self, uuid='', path=''):
        if not uuid:
            self.error = 'Se requiere el UUID para recupear su QR'
            return False
        id_transaccion = random.randint(1, 1000000000)
        token = self._getToken(id_transaccion)
        if not token:
            return False
        try:
            servicio = self._servicio_repositorio
            metodo = 'ObtenerQR'
            if self.solo_timbrar:
                servicio = self._servicio_timbrado
                metodo = 'ObtenerQRTimbrado'
            client = SoapClient(wsdl=servicio, ns=PREFIX_NS, soap_ns=PREFIX_SOAP)
            retval = getattr(client, metodo)(
                RFC = self.rfc,
                Token = token, 
                TransaccionID = id_transaccion,
                UUID = uuid
            )
        except SoapFault as sf:
            self.error = sf.faultcode, sf.faultstring, sf.message
            return False
        else:            
            self.qr = base64.decodestring(retval['QR']['Imagen'])
            if path:
                path = os.path.join(path, '%s.bmp' % uuid)
                f = open(path, 'wb')
                f.write(self.qr)
                f.close()
            return True
            
    def EstatusXML(self, uuid='', id_original=0):
        if not uuid and not id_original:
            self.error = 'Se requiere el UUID o el ID de la transacción original'
            return False
        if uuid:
            id_original = 0
        id_transaccion = random.randint(1, 1000000000)
        token = self._getToken(id_transaccion)
        if not token:
            return False
        try:
            servicio = self._servicio_repositorio
            metodo = 'EstatusComprobante'
            if self.solo_timbrar:
                servicio = self._servicio_timbrado
                metodo = 'EstatusTimbrado'
            client = SoapClient(wsdl=servicio, ns=PREFIX_NS, soap_ns=PREFIX_SOAP)
            retval = getattr(client, metodo)(
                RFC = self.rfc,
                Token = token, 
                TransaccionID = id_transaccion,
                TransaccionOriginal = id_original,
                UUID=uuid
            )
            return retval['Estatus']
        except SoapFault as sf:
            self.error = sf.faultcode, sf.faultstring, sf.message
            return False            

    def ClienteEstatus(self):       
        id_transaccion = random.randint(1, 1000000000)
        try:
            token = self._getToken(id_transaccion)
            if not token:
                return False
            client = SoapClient(
                wsdl = self._servicio_clientes,
                ns = PREFIX_NS,
                soap_ns = PREFIX_SOAP
            )                
            retval = client.EstatusCuenta(
                RFC = self.rfc,
                Token = token, 
                TransaccionID = id_transaccion,
            )
            return retval['Estatus']
        except SoapFault as sf:
            self.error = sf
            return False

    def ClienteRegistrar(self, data={}):       
        id_transaccion = random.randint(1, 1000000000)
        try:
            token = self._getToken(id_transaccion, True)
            if not token:
                return False
            client = SoapClient(
                wsdl = self._servicio_clientes,
                ns = PREFIX_NS,
                soap_ns = PREFIX_SOAP
            )
            retval = client.Registrar(
                Emisor = {
                    'RFC': data['rfc'],
                    'RazonSocial': data['razonsocial'],
                    'Nombre': '',
                    'CorreoElectronico': data['correo']},
                RfcIntegrador = self._rfc_integrador,
                Token = token, 
                TransaccionID = id_transaccion
            )
            return retval
        except SoapFault as sf:
            self.error = sf
            return False

    def ClienteAsignacionTimbres(self, timbres):       
        id_transaccion = random.randint(1, 1000000000)
        try:
            token = self._getToken(id_transaccion)
            if not token:
                return False
            client = SoapClient(
                wsdl = self._servicio_clientes,
                ns = PREFIX_NS,
                soap_ns = PREFIX_SOAP
            )
            retval = client.AsignacionTimbres(
                RFC = self.rfc,
                TimbresAsignar = timbres,
                Token = token, 
                TransaccionId = id_transaccion
            )
            return retval
        except SoapFault as sf:
            self.error = sf
            return False

